/******************************************************************************
	This file is a part of the Snapshot Bioinformatics Project

	Copyright (C) 2012-2013 Jarom Schow

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

#include "SqlInsertBuilder.h"

#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>

#include "Core.h"
#include "SqlServer.h"

using namespace Bio;

const int		c_maxQuerySize = 0x00040000; // 256 kbytes
const int		c_spacing = 2;
const QString	c_separator( ", " );

///////////////////////////////////////////////////////////////////////////////
//	SqlInsertBuilder
///////////////////////////////////////////////////////////////////////////////

/**
	@class SqlInsertBuilder
	@brief A convenience class for building multi-row insert statements.

	The SqlInsertBuilder class is designed to efficiently manage very large data 
	inserts in the database. It is created with a partial insert statement, and then
	data is added one row at a time. When the maximum query size is reached, the data 
	is sent to the database as a single query, and the builder begins creating a new 
	query.
**/

/**
	@brief	General Constructor.
	Creates a new insert builder with the given SQL statement that will insert data 
	into the given database. The SQL statement should be in the form: "INSERT INTO table_name
	(field_name1, field_name2, field_name3, ...) VALUES ".
	@param	sql	The insert statement.
	@param	db	The database to insert data into.
**/
SqlInsertBuilder::SqlInsertBuilder( const QString &sql, SqlServer *db ) : m_sql( sql ), m_server( db ), m_querySize( 0 )
{
}

/**
**/
SqlInsertBuilder::~SqlInsertBuilder()
{
}

/**
**/
bool SqlInsertBuilder::addValue( int val )
{
	m_values.append( QString::number( val ) );

	return true;
}

/**
**/
bool SqlInsertBuilder::addValue( const QString &val )
{
	m_values.append( m_server->esc( val ) );

	return true;
}

/**
**/
bool SqlInsertBuilder::addNullValue()
{
	m_values.append( "NULL" );

	return true;
}

/**
**/
bool SqlInsertBuilder::addRow()
{
	QString	values = "(" + m_values.join( ", " ) + ")";

	m_values.clear();

	return addRow( values );
}

/**
**/
bool SqlInsertBuilder::addRow( const QString &values )
{
	if( values.isEmpty() )
		return true;

	m_querySize += values.length() + c_spacing;

	if( m_querySize > c_maxQuerySize ){
		if( !flush() )
			return false;
		m_querySize += values.length();
	}

	m_rows.append( values );

	return true;
}

/**
**/
bool SqlInsertBuilder::flush()
{
	if( m_rows.isEmpty() )
		return true; // nothing to do

	bool		status = true;
	QString		sql = m_sql + " " + m_rows.join( c_separator );
	QSqlQuery	query( m_server->getDatabase() );

	if( !query.exec( sql ) ){
		QString	sqlError = "SQL error: " + query.lastError().text();

		Core::error( sqlError );
		Core::log( "SQL statement: " + sql );
		status = false;
	}

	m_rows.clear();
	m_querySize = m_sql.length();

	return status;
}
